pub fn math(op: fn(i32, i32) -> i32, a: i32, b: i32) -> i32 {
    op(a, b)
}

fn sum(a: i32, b: i32) -> i32 {
    a + b
}

fn product(a: i32, b: i32) -> i32 {
    a * b
}

fn is_true() -> bool {
    true
}

fn true_maker() -> fn() -> bool {
    is_true
}

fn closure_math<F: Fn() -> i32>(op: F) -> i32 {
    op()
}

fn two_times_impl() -> impl Fn(i32) -> i32 {
    let i = 2;
    move |j| j * i
}

#[cfg(test)]
mod tests {
    use crate::chapter2::{math, sum, product, true_maker, closure_math, two_times_impl};

    // 函数作为参数
    #[test]
    fn test_math() {
        let a = 2;
        let b = 3;
        assert_eq!(math(sum, a, b), 5);
        assert_eq!(math(product, a, b), 6);
    }

    // 函数作为返回值
    #[test]
    fn test_true_maker() {
        assert_eq!(true_maker()(), true);
    }

    // 闭包
    #[test]
    fn test_closure() {
        let out = 42;
        fn add(i: i32, j: i32) -> i32 {
            i + j
        }
        let closure_annotated = |i: i32, j: i32| -> i32 {
            i + j + out
        };
        let closure_inferred = |i, j| i + j + out;
        let i = 1;
        let j = 2;
        assert_eq!(3, add(i, j));
        assert_eq!(45, closure_annotated(i, j));
        assert_eq!(45, closure_inferred(i, j));
    }

    // 闭包作为参数
    #[test]
    fn test_closure_math() {
        let a = 2;
        let b = 3;
        assert_eq!(closure_math(|| a + b), 5);
        assert_eq!(closure_math(|| a * b), 6);
    }

    // 闭包作为返回值
    #[test]
    fn test_two_times_impl() {
        let result = two_times_impl();
        assert_eq!(result(2), 4);
    }

    // 使用 match 表达式
    #[test]
    fn test_match() {
        let mut v = vec![1, 2, 3, 4, 5];
        loop {
            match v.pop() {
                Some(x) => println!("{}", x),
                None => break,
            }
        }

        println!("=======分割线=======");

        // 使用 while let 简化代码
        let mut v = vec![1, 2, 3, 4, 5];
        while let Some(x) = v.pop() {
            println!("{}", x);
        }
    }

    // 字符串示例
    #[test]
    fn test_str() {
        // &'static str 是静态生命周期字符串
        let truth: &'static str = "Rust 是一门优雅的语言";
        let ptr = truth.as_ptr();
        println!("{:p}", ptr);
        let len = truth.len();
        assert_eq!(29, len);

        let s = unsafe {
            let slice = std::slice::from_raw_parts(ptr, len);
            std::str::from_utf8(slice)
        };
        assert_eq!(s, Ok(truth));
    }

    // 原生指针示例
    #[test]
    fn test_raw_pointer() {
        let mut x = 10;
        println!("{}", &mut x); // 10
        // 将 &mut x 可变引用 转换为 *mut i32 可变原生指针
        let ptr_x = &mut x as *mut i32;
        println!("{:?}", ptr_x); // 0x700004f42684
        // Box::new(20) 代表在堆内存上存储数字20
        let y = Box::new(20);
        let ptr_y = &*y as *const i32;
        unsafe {
            // 对 ptr_x 和 ptr_y 指针解引用，将两个指针指向的值求和
            *ptr_x += *ptr_y;
        }
        assert_eq!(x, 30);
    }

    // 单元结构体示例
    // 在 Debug 模式下，x,y和z是不同的内存地址
    // 在 Release 模式下，x,y和z是相同的内存地址
    #[test]
    fn test_unit_like_struct() {
        struct Empty;
        let x = Empty;
        println!("{:p}", &x);
        let y = x;
        println!("{:p}", &y);
        let z = Empty;
        println!("{:p}", &z);
        // 在标准库中表示全范围的RangeFull就是一个单元结构体
        assert_eq!((..), std::ops::RangeFull);
    }

    // 枚举体
    #[test]
    fn test_enum() {
        // 无参枚举
        enum Number {
            ZERO,
            ONE,
            TWO,
        }
        // 类 C 枚举
        enum Color {
            Red = 0xff0000,
            Green = 0x00ff00,
            Blue = 0x0000ff,
        }
        println!("roses are #{:06x}", Color::Red as i32);
        // 带参数枚举体。这样的枚举值本质上属于函数指针类型
        enum IpAddr {
            V4(u8, u8, u8, u8),
            V6(String),
        }
        let _x : fn(u8, u8, u8, u8) -> IpAddr = IpAddr::V4;
        let _y : fn(String) -> IpAddr = IpAddr::V6;
        let _home = IpAddr::V4(127, 0, 0, 1);

        // Option<T> 示例
        let s: &Option<String> = &Some("hello".to_string());
        // rust 2015 版本
        match s {
            &Some(ref s) => println!("s is : {}", s),
            _ => (),
        };
        // rust 2018 版本
        match s {
            Some(s) => println!("s is : {}", s),
            _ => (),
        };
    }

    // Box<T> 在堆内存中分配值的示例
    #[test]
    fn test_box() {
        // #[derive(PartialEq)] 是属性，可以让结构体自动实现 PartialEq trait. 类似的还有：Debug
        #[derive(PartialEq, Debug)]
        struct Point {
            x: f64,
            y: f64,
        }
        let box_point = Box::new(Point{x: 0.0, y: 0.0});
        let unboxed_point: Point = *box_point;
        assert_eq!(unboxed_point, Point{x: 0.0, y: 0.0});
    }
}